const path = require('path');
const crypto = require('crypto');
const fs = require('fs');
const schedule = require('node-schedule');
const moment = require('moment');
const sendEmail = require('../libs/sendEmail');
const copyDataLocation = path.join(__dirname, './dataLocation');
const tables = ['accountbooks', 'accountusers'];
const child_process = require('child_process');
let today;
const tokenObj = require('./baiduyunauth');

let logs = [];

const fsState = promisify(fs.stat);

function log(msg) {
  const now = moment().format('YYYY-MM-DD HH:mm:ss:ms');
  msg = `[${now}] ${msg}`;
  console.log(msg);
  logs.push(msg);
}

// 读取文件的MD5
const readFileMd5 = (url) => {
  return new Promise((reslove) => {
    let md5sum = crypto.createHash('md5');
    let stream = fs.createReadStream(url);
    stream.on('data', function (chunk) {
      md5sum.update(chunk);
    });
    stream.on('end', function () {
      let fileMd5 = md5sum.digest('hex');
      reslove(fileMd5);
    });
  });
};

/*
* 发送邮件
* */
async function email(isSuc) {
  const now = moment().format('YYYY-MM-DD HH:mm:ss');
  let emailContent = `
    <h1 style="text-align: center;">${now}的数据备份${isSuc ? '成功' : '失败'}</h1>
  `;
  emailContent += logs.map(v => `<p>${v}</p>`).join('');

  await sendEmail(`${now}数据备份`, emailContent);
}

/**
 * 执行shell命令
 * */
function exec(shell, msg, flag) {
  return new Promise(s => {
    child_process.exec(shell, function (error, stdout, stderr) {
      if (error) {
        log(msg + '失败');
        log(error.stack);
        s(0);
        return;
      }
      log(msg + '成功');
      if (flag) {
        s(stdout);
      } else {
        s(1);
      }
    });
  });
}

/*
* 异步函数同步化
* */
function promisify(method, ctx) {
  return function () {
    // 获取method调用的需要参数
    var args = Array.prototype.slice.call(arguments, 0);

    // use runtime this if ctx not provided
    ctx = ctx || this;

    //返回一个新的Promise对象
    return new Promise(function (resolve, reject) {
      // 除了函数传入的参数以外还需要一个callback函数来供异步方法调用
      var callback = function () {
        return function (err, result) {
          if (err) {
            return reject(err);
          }
          return resolve(result);
        };
      };
      args.push(callback());
      // 调用method
      method.apply(ctx, args);
    });
  };
};

/*
* 删除旧的文件
* */
async function delOldFile() {
  const shell = `rm -f ${copyDataLocation}/*`;
  return exec(shell, '删除旧文件');
}

/*
* 数据备份
* */
async function dataCopy(tableName) {
  const fileName = path.join(copyDataLocation, tableName + '-' + today + '.json');
  const shell = `mongoexport -u DB1 -p DB123 -d DB1 -c ${tableName} -o ${fileName} --type json`;
  // const shell = `mongoexport -d localTest -c ${tableName} -o ${fileName} --type json`;
  return await exec(shell, `备份&lt; ${tableName} &gt;文件`);
}

/*
* 测试创建文件
* */
async function createFile(tableName) {
  const fileName = path.join(copyDataLocation, tableName + '-' + today + '.json');
  const shell = `touch ${fileName}`;
  return await exec(shell, `新建&lt; ${tableName} &gt;文件`);
}

/*
* 打包压缩文件
* */
async function zipFiles() {
  const shell = `cd ${copyDataLocation} &&  zip -q -r ${today}.zip ./`;
  return await exec(shell, `压缩&lt; ${today}.zip &gt;文件`);
}

/*
* 数据文件上传 第一步 创建文件
* */
async function uploadData() {
  const filePath = path.join(copyDataLocation, today + '.zip');
  let uploadid;
  // 获取文件大小和名称
  const fileMsg = await fsState(filePath);
  // 获取文件的MD5
  const fileMD5 = await readFileMd5(filePath);
  const shell1 = `curl "http://pan.baidu.com/rest/2.0/xpan/file?method=precreate&access_token=${tokenObj.access_token}" -d 'path=/serverData/${today}.zip&size=${fileMsg.size}&isdir=0&autoinit=1&rtype=3&block_list=["${fileMD5}"]'`;
  let reqest = await exec(shell1, '文件预上传', 1);
  reqest = JSON.parse(reqest);
  if (reqest.errno === -6) {
    log('上传文件失败, token过期, 请手动更新token');
    return 0;
  }
  if (reqest.errno !== 0) {
    log('上传文件失败, 错误信息' + JSON.stringify(reqest));
    return 0;
  }
  uploadid = reqest.uploadid;
  const shell2 = `curl -F 'file=@${filePath}' "https://d.pcs.baidu.com/rest/2.0/pcs/superfile2?access_token=${tokenObj.access_token}&method=upload&type=tmpfile&path=/serverData/${today}.zip&uploadid=${uploadid}&partseq=0"`;
  reqest = await exec(shell2, '文件上传', 1);
  reqest = JSON.parse(reqest);
  if (reqest.errno === -6) {
    log('上传文件失败, token过期, 请手动更新token');
    return 0;
  }
  if (typeof reqest.errno !== 'undefined' && reqest.errno !== 0) {
    log('上传文件失败, 错误信息' + JSON.stringify(reqest));
    return 0;
  }
  const shell3 = `curl "https://pan.baidu.com/rest/2.0/xpan/file?method=create&access_token=${tokenObj.access_token}" -d 'path=/serverData/${today}.zip&size=${fileMsg.size}&isdir=0&rtype=3&uploadid=${uploadid}&block_list=["${reqest.md5}"]'`;
  reqest = await exec(shell3, '文件创建', 1);
  reqest = JSON.parse(reqest);
  if (reqest.errno === -6) {
    log('上传文件失败, token过期, 请手动更新token');
    return 0;
  }
  if (typeof reqest.errno !== 'undefined' && reqest.errno !== 0) {
    log('上传文件失败, 错误信息' + JSON.stringify(reqest));
    return 0;
  }
  return 1;
}

async function main() {
  let flag = await delOldFile();
  if (!flag) {
    return email(false);
  }
  for await (let v of tables) {
    flag = await dataCopy(v);
    if (!flag) {
      return email(false);
    }
  }
  flag = await zipFiles();
  if (!flag) {
    return email(false);
  }
  flag = await uploadData();
  if (!flag) {
    return email(false);
  }
  return email(true);
}

function start() {
  schedule.scheduleJob('00 00 23 * * *', function () {
    today = moment().format('YYYY-MM-DD');
    logs = [];
    main();
  });
}

start();
